/*
 * 11/19/04 1.0 moved to LGPL.
 * 
 * 18/06/01  Michael Scheerer,  Fixed bugs which causes
 *           negative indexes in method huffmann_decode and in method 
 *           dequanisize_sample.
 *
 * 16/07/01  Michael Scheerer, Catched a bug in method
 *           huffmann_decode, which causes an outOfIndexException.
 *           Cause : Indexnumber of 24 at SfBandIndex,
 *           which has only a length of 22. I have simply and dirty 
 *           fixed the index to <= 22, because I'm not really be able
 *           to fix the bug. The Indexnumber is taken from the MP3 
 *           file and the origin Ma-Player with the same code works 
 *           well.      
 * 
 * 02/19/99  Java Conversion by E.B, javalayer@javazoom.net
 *-----------------------------------------------------------------------
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as published
 *   by the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *----------------------------------------------------------------------
 */
package org.mp3transform;

import java.io.IOException;

import org.mp3transform.Constants.SBI;

/**
 * Class Implementing Layer 3 Decoder.
 * http://www.oreilly.com/catalog/mp3/chapter/ch02.html Maximum bitreservoir is
 * 511 byte.
 * http://www.hydrogenaudio.org/forums/lofiversion/index.php/t42194.html gr:
 * granules (sub-frames)
 */
final class Layer3Decoder {
    static class GrInfo {
        int part23Length;
        int bigValues;
        int globalGain;
        int scaleFactorCompress;
        boolean windowSwitching;
        int blockType;
        boolean mixedBlock;
        int[] tableSelect = new int[3];
        int[] subblockGain = new int[3];
        int region0Count;
        int region1Count;
        int preflag;
        int scaleFactorScale;
        int count1TableSelect;
    }

    static class Channel {
        int[] scfsi = new int[4];
        GrInfo[] gr = new GrInfo[] { new GrInfo(), new GrInfo() };
    }

    static class SideInfo {
        int mainDataBegin = 0;
        Channel[] ch = new Channel[] { new Channel(), new Channel() };
    }

    static class ScaleFactor {
        int[] l = new int[23]; /* [cb] */
        int[][] s = new int[3][13]; /* [window][cb] */
    }

    private static final int SSLIMIT = 18;
    private static final int SBLIMIT = 32;
    // DOUBLE
    private static final double D43 = (4.0 / 3.0);
    private final int[] scaleFactorBuffer = new int[54];
    // TODO why +4?
    private final int[] is1d = new int[SBLIMIT * SSLIMIT + 4];
    private final double[][] ro0 = new double[SBLIMIT][SSLIMIT];
    private final double[][] ro1 = new double[SBLIMIT][SSLIMIT];
    private final double[][] lr0 = new double[SBLIMIT][SSLIMIT];
    private final double[][] lr1 = new double[SBLIMIT][SSLIMIT];
    private final double[] out1d = new double[SBLIMIT * SSLIMIT];
    private final double[][] prevBlock = new double[2][SBLIMIT * SSLIMIT];
    private final double[] k0 = new double[SBLIMIT * SSLIMIT];
    private final double[] k1 = new double[SBLIMIT * SSLIMIT];
    private final int[] nonzero = new int[2];
    private final Bitstream stream;
    private final Header header;
    private final SynthesisFilter filter1, filter2;
    private final Decoder player;
    private final BitReservoir br = new BitReservoir();
    private final SideInfo si = new SideInfo();
    private final ScaleFactor[] scaleFactors = new ScaleFactor[] { new ScaleFactor(), new ScaleFactor() };
    private int maxGr;
    private int frameStart;
    private int part2Start;
    private int channels;
    private int firstChannel;
    private int lastChannel;
    private int sfreq;
    private final int[] isPos = new int[576];
    private final double[] isRatio = new double[576];
    private final double[] tsOutCopy = new double[18];
    private final double[] rawout = new double[36];
    // subband samples are buffered and passed to the
    // SynthesisFilter in one go.
    private double[] samples1 = new double[32];
    private double[] samples2 = new double[32];
    private final int[] newSlen = new int[4];
    int x, y, v, w;

    public Layer3Decoder(Bitstream stream, Header header, SynthesisFilter filter1, SynthesisFilter filter2,
            Decoder player) {
        this.stream = stream;
        this.header = header;
        this.filter1 = filter1;
        this.filter2 = filter2;
        this.player = player;
        channels = (header.mode() == Header.MODE_SINGLE_CHANNEL) ? 1 : 2;
        maxGr = (header.version() == Header.VERSION_MPEG1) ? 2 : 1;
        sfreq = header.sampleFrequency()
                + ((header.version() == Header.VERSION_MPEG1) ? 3 : (header.version() == Header.VERSION_MPEG25_LSF) ? 6
                        : 0);
        if (channels == 2) {
            firstChannel = 0;
            lastChannel = 1;
        }
        nonzero[0] = nonzero[1] = 576;
    }

    public void decodeFrame() throws IOException {
        int slots = header.slots();
        getSideInfo();
        int flushMain = br.getBitCount() & 7;
        if (flushMain != 0) {
            br.getBits(8 - flushMain);
        }
        int mainDataEnd = br.getBitCount() >>> 3; // of previous frame
        for (int i = 0; i < slots; i++) {
            br.putByte(stream.getBits(8));
        }
        int bytesToDiscard = frameStart - mainDataEnd - si.mainDataBegin;
        frameStart += slots;
        if (bytesToDiscard < 0) {
            return;
        }
        if (mainDataEnd > 4096) {
            frameStart -= 4096;
            br.rewindBytes(4096);
        }
        for (; bytesToDiscard > 0; bytesToDiscard--) {
            br.getBits(8);
        }
        for (int gr = 0; gr < maxGr; gr++) {
            for (int ch = 0; ch < channels; ch++) {
                part2Start = br.getBitCount();
                if (header.version() == Header.VERSION_MPEG1) {
                    getScaleFactors(ch, gr);
                } else {
                    // MPEG-2 LSF, MPEG-2.5 LSF
                    getLsfScaleFactors(ch, gr);
                }
                huffmanDecode(ch, gr);
                dequantizeSample(ch == 0 ? ro0 : ro1, ch, gr);
            }
            stereo(gr);
            for (int ch = firstChannel; ch <= lastChannel; ch++) {
                reorder(ch == 0 ? lr0 : lr1, ch, gr);
                antialias(ch, gr);
                hybrid(ch, gr);
                for (int sb18 = 18; sb18 < 576; sb18 += 36) {
                    // Frequency inversion
                    for (int ss = 1; ss < SSLIMIT; ss += 2) {
                        out1d[sb18 + ss] = -out1d[sb18 + ss];
                    }
                }
                if (ch == 0) {
                    for (int ss = 0; ss < SSLIMIT; ss++) {
                        // Polyphase synthesis
                        for (int sb18 = 0, sb = 0; sb18 < 576; sb18 += 18, sb++) {
                            samples1[sb] = out1d[sb18 + ss];
                        }
                        filter1.calculatePcmSamples(samples1, player);
                    }
                } else {
                    for (int ss = 0; ss < SSLIMIT; ss++) {
                        // Polyphase synthesis
                        for (int sb18 = 0, sb = 0; sb18 < 576; sb18 += 18, sb++) {
                            samples2[sb] = out1d[sb18 + ss];
                        }
                        filter2.calculatePcmSamples(samples2, player);
                    }
                }
            }
        }
    }

    /**
     * Reads the side info from the stream, assuming the entire frame has been
     * read already. Mono : 136 bits (= 17 bytes) Stereo : 256 bits (= 32 bytes)
     */
    private void getSideInfo() throws IOException {
        if (header.version() == Header.VERSION_MPEG1) {
            si.mainDataBegin = stream.getBits(9);
            if (channels == 1) {
                stream.getBits(5);
            } else {
                stream.getBits(3);
            }
            for (int ch = 0; ch < channels; ch++) {
                Channel c = si.ch[ch];
                c.scfsi[0] = stream.getBits(1);
                c.scfsi[1] = stream.getBits(1);
                c.scfsi[2] = stream.getBits(1);
                c.scfsi[3] = stream.getBits(1);
            }
            for (int gr = 0; gr < 2; gr++) {
                for (int ch = 0; ch < channels; ch++) {
                    GrInfo gi = si.ch[ch].gr[gr];
                    gi.part23Length = stream.getBits(12);
                    gi.bigValues = stream.getBits(9);
                    gi.globalGain = stream.getBits(8);
                    gi.scaleFactorCompress = stream.getBits(4);
                    gi.windowSwitching = stream.getBits(1) != 0;
                    if (gi.windowSwitching) {
                        gi.blockType = stream.getBits(2);
                        gi.mixedBlock = stream.getBits(1) != 0;
                        gi.tableSelect[0] = stream.getBits(5);
                        gi.tableSelect[1] = stream.getBits(5);
                        gi.subblockGain[0] = stream.getBits(3);
                        gi.subblockGain[1] = stream.getBits(3);
                        gi.subblockGain[2] = stream.getBits(3);
                        // Set regionCount: implicit in this case
                        if (gi.blockType == 0) {
                            throw new IOException("Side info bad: blockType == 0 in split block");
                        } else if (gi.blockType == 2 && !gi.mixedBlock) {
                            gi.region0Count = 8;
                        } else {
                            gi.region0Count = 7;
                        }
                        gi.region1Count = 20 - gi.region0Count;
                    } else {
                        gi.tableSelect[0] = stream.getBits(5);
                        gi.tableSelect[1] = stream.getBits(5);
                        gi.tableSelect[2] = stream.getBits(5);
                        gi.region0Count = stream.getBits(4);
                        gi.region1Count = stream.getBits(3);
                        gi.blockType = 0;
                    }
                    gi.preflag = stream.getBits(1);
                    gi.scaleFactorScale = stream.getBits(1);
                    gi.count1TableSelect = stream.getBits(1);
                }
            }
        } else { // MPEG-2 LSF, MPEG-2.5 LSF
            si.mainDataBegin = stream.getBits(8);
            if (channels == 1) {
                stream.getBits(1);
            } else {
                stream.getBits(2);
            }
            for (int ch = 0; ch < channels; ch++) {
                GrInfo gi = si.ch[ch].gr[0];
                gi.part23Length = stream.getBits(12);
                gi.bigValues = stream.getBits(9);
                gi.globalGain = stream.getBits(8);
                gi.scaleFactorCompress = stream.getBits(9);
                gi.windowSwitching = stream.getBits(1) != 0;
                if (gi.windowSwitching) {
                    gi.blockType = stream.getBits(2);
                    gi.mixedBlock = stream.getBits(1) != 0;
                    gi.tableSelect[0] = stream.getBits(5);
                    gi.tableSelect[1] = stream.getBits(5);
                    gi.subblockGain[0] = stream.getBits(3);
                    gi.subblockGain[1] = stream.getBits(3);
                    gi.subblockGain[2] = stream.getBits(3);
                    // Set regionCount: implicit in this case
                    if (gi.blockType == 0) {
                        throw new IOException("Side info bad: blockType == 0 in split block");
                    } else if (gi.blockType == 2 && !gi.mixedBlock) {
                        gi.region0Count = 8;
                    } else {
                        gi.region0Count = 7;
                        gi.region1Count = 20 - gi.region0Count;
                    }
                } else {
                    gi.tableSelect[0] = stream.getBits(5);
                    gi.tableSelect[1] = stream.getBits(5);
                    gi.tableSelect[2] = stream.getBits(5);
                    gi.region0Count = stream.getBits(4);
                    gi.region1Count = stream.getBits(3);
                    gi.blockType = 0;
                }
                gi.scaleFactorScale = stream.getBits(1);
                gi.count1TableSelect = stream.getBits(1);
            }
        }
    }

    private void getScaleFactors(int ch, int gr) {
        int sfb, window;
        GrInfo gi = si.ch[ch].gr[gr];
        int scaleComp = gi.scaleFactorCompress;
        int[][] slen = Constants.SLEN;
        int length0 = slen[0][scaleComp];
        int length1 = slen[1][scaleComp];
        ScaleFactor sfc = scaleFactors[ch];
        int[] sfl = sfc.l;
        int[][] sfs = sfc.s;
        if (gi.windowSwitching && gi.blockType == 2) {
            if (gi.mixedBlock) {
                for (sfb = 0; sfb < 8; sfb++) {
                    sfl[sfb] = br.getBits(slen[0][gi.scaleFactorCompress]);
                }
                for (sfb = 3; sfb < 6; sfb++) {
                    for (window = 0; window < 3; window++) {
                        sfs[window][sfb] = br.getBits(slen[0][gi.scaleFactorCompress]);
                    }
                }
                for (sfb = 6; sfb < 12; sfb++) {
                    for (window = 0; window < 3; window++) {
                        sfs[window][sfb] = br.getBits(slen[1][gi.scaleFactorCompress]);
                    }
                }
                for (sfb = 12, window = 0; window < 3; window++) {
                    sfs[window][sfb] = 0;
                }
            } else { // SHORT
                sfs[0][0] = br.getBits(length0);
                sfs[1][0] = br.getBits(length0);
                sfs[2][0] = br.getBits(length0);
                sfs[0][1] = br.getBits(length0);
                sfs[1][1] = br.getBits(length0);
                sfs[2][1] = br.getBits(length0);
                sfs[0][2] = br.getBits(length0);
                sfs[1][2] = br.getBits(length0);
                sfs[2][2] = br.getBits(length0);
                sfs[0][3] = br.getBits(length0);
                sfs[1][3] = br.getBits(length0);
                sfs[2][3] = br.getBits(length0);
                sfs[0][4] = br.getBits(length0);
                sfs[1][4] = br.getBits(length0);
                sfs[2][4] = br.getBits(length0);
                sfs[0][5] = br.getBits(length0);
                sfs[1][5] = br.getBits(length0);
                sfs[2][5] = br.getBits(length0);
                sfs[0][6] = br.getBits(length1);
                sfs[1][6] = br.getBits(length1);
                sfs[2][6] = br.getBits(length1);
                sfs[0][7] = br.getBits(length1);
                sfs[1][7] = br.getBits(length1);
                sfs[2][7] = br.getBits(length1);
                sfs[0][8] = br.getBits(length1);
                sfs[1][8] = br.getBits(length1);
                sfs[2][8] = br.getBits(length1);
                sfs[0][9] = br.getBits(length1);
                sfs[1][9] = br.getBits(length1);
                sfs[2][9] = br.getBits(length1);
                sfs[0][10] = br.getBits(length1);
                sfs[1][10] = br.getBits(length1);
                sfs[2][10] = br.getBits(length1);
                sfs[0][11] = br.getBits(length1);
                sfs[1][11] = br.getBits(length1);
                sfs[2][11] = br.getBits(length1);
                sfs[0][12] = 0;
                sfs[1][12] = 0;
                sfs[2][12] = 0;
            } // SHORT
        } else { // LONG types 0,1,3
            if ((si.ch[ch].scfsi[0] == 0) || (gr == 0)) {
                sfl[0] = br.getBits(length0);
                sfl[1] = br.getBits(length0);
                sfl[2] = br.getBits(length0);
                sfl[3] = br.getBits(length0);
                sfl[4] = br.getBits(length0);
                sfl[5] = br.getBits(length0);
            }
            if ((si.ch[ch].scfsi[1] == 0) || (gr == 0)) {
                sfl[6] = br.getBits(length0);
                sfl[7] = br.getBits(length0);
                sfl[8] = br.getBits(length0);
                sfl[9] = br.getBits(length0);
                sfl[10] = br.getBits(length0);
            }
            if ((si.ch[ch].scfsi[2] == 0) || (gr == 0)) {
                sfl[11] = br.getBits(length1);
                sfl[12] = br.getBits(length1);
                sfl[13] = br.getBits(length1);
                sfl[14] = br.getBits(length1);
                sfl[15] = br.getBits(length1);
            }
            if ((si.ch[ch].scfsi[3] == 0) || (gr == 0)) {
                sfl[16] = br.getBits(length1);
                sfl[17] = br.getBits(length1);
                sfl[18] = br.getBits(length1);
                sfl[19] = br.getBits(length1);
                sfl[20] = br.getBits(length1);
            }
            sfl[21] = 0;
            sfl[22] = 0;
        }
    }

    private void getLsfScaleData(int ch, int gr) {
        int scaleFactorComp, intScalefacComp;
        int modeExt = header.modeExtension();
        int blockTypeNumber;
        int blockNumber = 0;
        GrInfo gi = si.ch[ch].gr[gr];
        scaleFactorComp = gi.scaleFactorCompress;
        if (gi.blockType == 2) {
            if (!gi.mixedBlock) {
                blockTypeNumber = 1;
            } else {
                blockTypeNumber = 2;
            }
        } else {
            blockTypeNumber = 0;
        }
        if (!(((modeExt == 1) || (modeExt == 3)) && (ch == 1))) {
            if (scaleFactorComp < 400) {
                newSlen[0] = (scaleFactorComp >>> 4) / 5;
                newSlen[1] = (scaleFactorComp >>> 4) % 5;
                newSlen[2] = (scaleFactorComp & 0xF) >>> 2;
                newSlen[3] = (scaleFactorComp & 3);
                si.ch[ch].gr[gr].preflag = 0;
                blockNumber = 0;
            } else if (scaleFactorComp < 500) {
                newSlen[0] = ((scaleFactorComp - 400) >>> 2) / 5;
                newSlen[1] = ((scaleFactorComp - 400) >>> 2) % 5;
                newSlen[2] = (scaleFactorComp - 400) & 3;
                newSlen[3] = 0;
                si.ch[ch].gr[gr].preflag = 0;
                blockNumber = 1;
            } else if (scaleFactorComp < 512) {
                newSlen[0] = (scaleFactorComp - 500) / 3;
                newSlen[1] = (scaleFactorComp - 500) % 3;
                newSlen[2] = 0;
                newSlen[3] = 0;
                si.ch[ch].gr[gr].preflag = 1;
                blockNumber = 2;
            }
        }
        if ((((modeExt == 1) || (modeExt == 3)) && (ch == 1))) {
            intScalefacComp = scaleFactorComp >>> 1;
            if (intScalefacComp < 180) {
                newSlen[0] = intScalefacComp / 36;
                newSlen[1] = (intScalefacComp % 36) / 6;
                newSlen[2] = (intScalefacComp % 36) % 6;
                newSlen[3] = 0;
                si.ch[ch].gr[gr].preflag = 0;
                blockNumber = 3;
            } else if (intScalefacComp < 244) {
                newSlen[0] = ((intScalefacComp - 180) & 0x3F) >>> 4;
                newSlen[1] = ((intScalefacComp - 180) & 0xF) >>> 2;
                newSlen[2] = (intScalefacComp - 180) & 3;
                newSlen[3] = 0;
                si.ch[ch].gr[gr].preflag = 0;
                blockNumber = 4;
            } else if (intScalefacComp < 255) {
                newSlen[0] = (intScalefacComp - 244) / 3;
                newSlen[1] = (intScalefacComp - 244) % 3;
                newSlen[2] = 0;
                newSlen[3] = 0;
                si.ch[ch].gr[gr].preflag = 0;
                blockNumber = 5;
            }
        }
        for (int x = 0; x < 45; x++) {
            // TODO: why 45, not 54?
            scaleFactorBuffer[x] = 0;
        }
        for (int i = 0, m = 0; i < 4; i++) {
            int len = Constants.NR_OF_SFB_BLOCK[blockNumber][blockTypeNumber][i];
            for (int j = 0; j < len; j++) {
                scaleFactorBuffer[m] = (newSlen[i] == 0) ? 0 : br.getBits(newSlen[i]);
                m++;
            }
        }
    }

    private void getLsfScaleFactors(int ch, int gr) {
        int m = 0;
        int sfb, window;
        GrInfo gi = si.ch[ch].gr[gr];
        getLsfScaleData(ch, gr);
        ScaleFactor sf = scaleFactors[ch];
        if (gi.windowSwitching && (gi.blockType == 2)) {
            if (gi.mixedBlock) {
                for (sfb = 0; sfb < 8; sfb++) {
                    sf.l[sfb] = scaleFactorBuffer[m];
                    m++;
                }
                for (sfb = 3; sfb < 12; sfb++) {
                    for (window = 0; window < 3; window++) {
                        sf.s[window][sfb] = scaleFactorBuffer[m];
                        m++;
                    }
                }
                for (window = 0; window < 3; window++) {
                    sf.s[window][12] = 0;
                }
            } else { // SHORT
                for (sfb = 0; sfb < 12; sfb++) {
                    for (window = 0; window < 3; window++) {
                        sf.s[window][sfb] = scaleFactorBuffer[m];
                        m++;
                    }
                }
                for (window = 0; window < 3; window++) {
                    sf.s[window][12] = 0;
                }
            }
        } else { // LONG types 0,1,3
            for (sfb = 0; sfb < 21; sfb++) {
                sf.l[sfb] = scaleFactorBuffer[m];
                m++;
            }
            sf.l[21] = 0;
            sf.l[22] = 0;
        }
    }

    private void huffmanDecode(final int ch, final int gr) {
        GrInfo gi = si.ch[ch].gr[gr];
        x = y = v = w = 0;
        int part23End = part2Start + gi.part23Length;
        int region1Start;
        int region2Start;
        int buf, buf1;
        Huffman huffman;
        // Find region boundary for short block case
        if (gi.windowSwitching && (gi.blockType == 2)) {
            // Region2.
            // MS: Extrahandling for 8KHZ
            region1Start = (sfreq == 8) ? 72 : 36; // sfb[9/3]*3=36 or in case
            // 8KHZ = 72
            region2Start = 576; // No Region2 for short block case
        } else { // Find region boundary for long block case
            buf = gi.region0Count + 1;
            buf1 = buf + gi.region1Count + 1;
            if (buf1 > Constants.SF_BAND_INDEX[sfreq].l.length - 1) {
                buf1 = Constants.SF_BAND_INDEX[sfreq].l.length - 1;
            }
            region1Start = Constants.SF_BAND_INDEX[sfreq].l[buf];
            region2Start = Constants.SF_BAND_INDEX[sfreq].l[buf1]; /* MI */
        }
        int index = 0;
        for (int i = 0; i < (gi.bigValues << 1); i += 2) {
            if (i < region1Start) {
                huffman = Huffman.HUFFMAN[gi.tableSelect[0]];
            } else if (i < region2Start) {
                huffman = Huffman.HUFFMAN[gi.tableSelect[1]];
            } else {
                huffman = Huffman.HUFFMAN[gi.tableSelect[2]];
            }
            huffman.decode(this, br);
            is1d[index++] = x;
            is1d[index++] = y;
        }
        // Read count1 area
        huffman = Huffman.HUFFMAN[gi.count1TableSelect + 32];
        int numBits = br.getBitCount();
        while ((numBits < part23End) && (index < 576)) {
            huffman.decode(this, br);
            is1d[index++] = v;
            is1d[index++] = w;
            is1d[index++] = x;
            is1d[index++] = y;
            numBits = br.getBitCount();
        }
        if (numBits > part23End) {
            br.rewindBits(numBits - part23End);
            index -= 4;
        }
        numBits = br.getBitCount();
        // Dismiss stuffing bits
        if (numBits < part23End) {
            br.getBits(part23End - numBits);
        }
        // Zero out rest
        if (index < 576) {
            nonzero[ch] = index;
        } else {
            nonzero[ch] = 576;
        }
        if (index < 0) {
            index = 0;
        }
        // may not be necessary
        for (; index < 576; index++) {
            is1d[index] = 0;
        }
    }

    private void iStereoKValues(int pos, int type, int i) {
        if (pos == 0) {
            k0[i] = 1.0f;
            k1[i] = 1.0f;
        } else if ((pos & 1) != 0) {
            k0[i] = Constants.IO[type][(pos + 1) >>> 1];
            k1[i] = 1.0f;
        } else {
            k0[i] = 1.0f;
            k1[i] = Constants.IO[type][pos >>> 1];
        }
    }

    private double getT43(int abv, double globalGain) {
        switch (abv) {
        case 0:
            return 0.0f;
        case 1:
            return globalGain;
        case -1:
            return -globalGain;
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
            return globalGain * Constants.T43[abv];
        case -2:
        case -3:
        case -4:
        case -5:
        case -6:
            return -globalGain * Constants.T43[-abv];
        default:
            if (abv > 0) {
                if (abv < Constants.T43_SIZE) {
                    return globalGain * Constants.T43[abv];
                }
                return globalGain * Math.pow(abv, D43);
            }
            if (-abv < Constants.T43_SIZE) {
                return -globalGain * Constants.T43[-abv];
            }
            return -globalGain * Math.pow(-abv, D43);
        }
    }

    private void dequantizeSample(double[][] xr, int ch, int gr) {
        GrInfo gi = si.ch[ch].gr[gr];
        int nextCb; // next critical band boundary
        Constants.SBI sbif = Constants.SF_BAND_INDEX[sfreq];
        int[] s = sbif.s;
        int[] l = sbif.l;
        int cbWidth = 0;
        int len = nonzero[ch];
        // Compute overall (global) scaling
        double globalGain = Constants.POW2[gi.globalGain];
        for (int i = 0, sb = 0; sb < SBLIMIT; sb++) {
            for (int ss = 0; ss < SSLIMIT; ss++, i++) {
                if (i >= len) {
                    break;
                }
                xr[sb][ss] = getT43(is1d[i], globalGain);
            }
        }
        // choose correct scalefactor band per block type, initalize boundary
        if (gi.windowSwitching && (gi.blockType == 2)) {
            if (gi.mixedBlock) {
                nextCb = l[1];
                // LONG blocks: 0,1,3
            } else {
                cbWidth = s[1];
                nextCb = (cbWidth << 2) - cbWidth;
            }
        } else {
            nextCb = l[1];
            // LONG blocks: 0,1,3
        }
        int cb = 0;
        int cbBegin = 0;
        int index = 0;
        // apply formula per block type
        for (int j = 0; j < len; j++) {
            if (index == nextCb) {
                // adjust critical band boundary
                if (gi.windowSwitching && gi.blockType == 2) {
                    if (gi.mixedBlock) {
                        if (index == l[8]) {
                            nextCb = s[4];
                            nextCb = (nextCb << 2) - nextCb;
                            cb = 3;
                            cbWidth = s[4] - s[3];
                            cbBegin = s[3];
                            cbBegin = (cbBegin << 2) - cbBegin;
                        } else if (index < l[8]) {
                            nextCb = l[(++cb) + 1];
                        } else {
                            nextCb = s[(++cb) + 1];
                            nextCb = (nextCb << 2) - nextCb;
                            cbBegin = s[cb];
                            cbWidth = s[cb + 1] - cbBegin;
                            cbBegin = (cbBegin << 2) - cbBegin;
                        }
                    } else {
                        nextCb = s[(++cb) + 1];
                        nextCb = (nextCb << 2) - nextCb;
                        cbBegin = s[cb];
                        cbWidth = s[cb + 1] - cbBegin;
                        cbBegin = (cbBegin << 2) - cbBegin;
                    }
                } else { // long blocks
                    nextCb = l[(++cb) + 1];
                }
            }
            int sb = j / SSLIMIT;
            int ss = j - sb * SSLIMIT; // % SSLIMIT
            // Do long/short dependent scaling operations
            int idx;
            if (gi.windowSwitching && gi.blockType == 2 && (!gi.mixedBlock || j >= 36)) {
                int ti = (index - cbBegin) / cbWidth;
                idx = scaleFactors[ch].s[ti][cb] << gi.scaleFactorScale;
                idx += (gi.subblockGain[ti] << 2);
            } else {
                // LONG block types 0,1,3 & 1st 2 subbands of switched blocks
                idx = scaleFactors[ch].l[cb];
                if (gi.preflag != 0) {
                    idx += Constants.PRETAB[cb];
                }
                idx = idx << gi.scaleFactorScale;
            }
            xr[sb][ss] *= Constants.TWO_TO_NEGATIVE_HALF_POW[idx];
            index++;
        }
        for (int j = len; j < 576; j++) {
            int sb = j / SSLIMIT;
            int ss = j - sb * SSLIMIT; // % SSLIMIT
            xr[sb][ss] = 0.0f;
        }
        return;
    }

    private void reorder(double[][] xr, int ch, int gr) {
        GrInfo gi = si.ch[ch].gr[gr];
        if (gi.windowSwitching && gi.blockType == 2) {
            for (int index = 0; index < 576; index++) {
                out1d[index] = 0.0f;
            }
            if (gi.mixedBlock) {
                // NO REORDER FOR LOW 2 SUBBANDS
                for (int index = 0; index < 36; index++) {
                    int sb = index / SSLIMIT;
                    int ss = index - sb * SSLIMIT; // % SSLIMIT
                    out1d[index] = xr[sb][ss];
                }
                // REORDERING FOR REST SWITCHED SHORT
                for (int sfb = 3; sfb < 13; sfb++) {
                    int sfbStart = Constants.SF_BAND_INDEX[sfreq].s[sfb];
                    int sfbLines = Constants.SF_BAND_INDEX[sfreq].s[sfb + 1] - sfbStart;
                    int sfbStart3 = (sfbStart << 2) - sfbStart;
                    for (int freq = 0, freq3 = 0; freq < sfbLines; freq++, freq3 += 3) {
                        int srcLine = sfbStart3 + freq;
                        int desLine = sfbStart3 + freq3;
                        int sb = srcLine / SSLIMIT;
                        int ss = srcLine - sb * SSLIMIT; // % SSLIMIT
                        out1d[desLine] = xr[sb][ss];
                        srcLine += sfbLines;
                        desLine++;
                        sb = srcLine / SSLIMIT;
                        ss = srcLine - sb * SSLIMIT; // % SSLIMIT
                        out1d[desLine] = xr[sb][ss];
                        srcLine += sfbLines;
                        desLine++;
                        sb = srcLine / SSLIMIT;
                        ss = srcLine - sb * SSLIMIT; // % SSLIMIT
                        out1d[desLine] = xr[sb][ss];
                    }
                }
            } else {
                // pure short
                int[] reorder = Constants.REORDER_TABLE[sfreq];
                for (int index = 0; index < 576; index++) {
                    int j = reorder[index];
                    int sb = j / SSLIMIT;
                    int ss = j - sb * SSLIMIT; // % SSLIMIT
                    out1d[index] = xr[sb][ss];
                }
            }
        } else {
            // long blocks
            for (int i = 0, sb = 0; sb < SBLIMIT; sb++) {
                for (int ss = 0; ss < SSLIMIT; ss++, i++) {
                    out1d[i] = xr[sb][ss];
                }
            }
        }
    }

    private void stereo(int gr) {
        if (channels == 1) { // mono , bypass xr[0][][] to lr[0][][]
            for (int sb = 0; sb < SBLIMIT; sb++) {
                for (int ss = 0; ss < SSLIMIT; ss += 3) {
                    lr0[sb][ss] = ro0[sb][ss];
                    lr0[sb][ss + 1] = ro0[sb][ss + 1];
                    lr0[sb][ss + 2] = ro0[sb][ss + 2];
                }
            }
            return;
        }
        GrInfo gi = si.ch[0].gr[gr];
        int modeExt = header.modeExtension();
        int sfb;
        int temp, temp2;
        boolean msStereo = ((header.mode() == Header.MODE_JOINT_STEREO) && ((modeExt & 0x2) != 0));
        boolean iStereo = ((header.mode() == Header.MODE_JOINT_STEREO) && ((modeExt & 0x1) != 0));
        boolean lsf = ((header.version() == Header.VERSION_MPEG2_LSF || header.version() == Header.VERSION_MPEG25_LSF));
        int ioType = (gi.scaleFactorCompress & 1);
        for (int i = 0; i < 576; i++) {
            isPos[i] = 7;
            isRatio[i] = 0.0f;
        }
        if (iStereo) {
            SBI sbif = Constants.SF_BAND_INDEX[sfreq];
            int[] s = sbif.s;
            int[] l = sbif.l;
            if (gi.windowSwitching && gi.blockType == 2) {
                if (gi.mixedBlock) {
                    int maxSfb = 0;
                    for (int j = 0; j < 3; j++) {
                        int sfbcnt = 2;
                        for (sfb = 12; sfb >= 3; sfb--) {
                            int i = s[sfb];
                            int lines = s[sfb + 1] - i;
                            i = (i << 2) - i + (j + 1) * lines - 1;
                            while (lines > 0) {
                                if (ro1[i / 18][i % 18] != 0.0f) {
                                    sfbcnt = sfb;
                                    sfb = -10;
                                    lines = -10;
                                }
                                lines--;
                                i--;
                            }
                        }
                        sfb = sfbcnt + 1;
                        if (sfb > maxSfb) {
                            maxSfb = sfb;
                        }
                        while (sfb < 12) {
                            temp = s[sfb];
                            int sb = s[sfb + 1] - temp;
                            int i = (temp << 2) - temp + j * sb;
                            for (; sb > 0; sb--) {
                                isPos[i] = scaleFactors[1].s[j][sfb];
                                if (isPos[i] != 7) {
                                    if (lsf) {
                                        iStereoKValues(isPos[i], ioType, i);
                                    } else {
                                        isRatio[i] = Constants.TAN12[isPos[i]];
                                    }
                                }
                                i++;
                            }
                            sfb++;
                        }
                        sfb = s[10];
                        int sb = s[11] - sfb;
                        sfb = (sfb << 2) - sfb + j * sb;
                        temp = s[11];
                        sb = s[12] - temp;
                        int i = (temp << 2) - temp + j * sb;
                        for (; sb > 0; sb--) {
                            isPos[i] = isPos[sfb];
                            if (lsf) {
                                k0[i] = k0[sfb];
                                k1[i] = k1[sfb];
                            } else {
                                isRatio[i] = isRatio[sfb];
                            }
                            i++;
                        }
                    }
                    if (maxSfb <= 3) {
                        int i = 2;
                        int ss = 17;
                        int sb = -1;
                        while (i >= 0) {
                            if (ro1[i][ss] != 0.0f) {
                                sb = (i << 4) + (i << 1) + ss;
                                i = -1;
                            } else {
                                ss--;
                                if (ss < 0) {
                                    i--;
                                    ss = 17;
                                }
                            }
                        }
                        i = 0;
                        while (l[i] <= sb) {
                            i++;
                        }
                        sfb = i;
                        i = l[i];
                        for (; sfb < 8; sfb++) {
                            sb = l[sfb + 1] - l[sfb];
                            for (; sb > 0; sb--) {
                                isPos[i] = scaleFactors[1].l[sfb];
                                if (isPos[i] != 7) {
                                    if (lsf) {
                                        iStereoKValues(isPos[i], ioType, i);
                                    } else {
                                        isRatio[i] = Constants.TAN12[isPos[i]];
                                    }
                                }
                                i++;
                            }
                        }
                    }
                } else {
                    for (int j = 0; j < 3; j++) {
                        int sfbcnt;
                        sfbcnt = -1;
                        for (sfb = 12; sfb >= 0; sfb--) {
                            temp = s[sfb];
                            int lines = s[sfb + 1] - temp;
                            int i = (temp << 2) - temp + (j + 1) * lines - 1;
                            while (lines > 0) {
                                if (ro1[i / 18][i % 18] != 0.0f) {
                                    sfbcnt = sfb;
                                    sfb = -10;
                                    lines = -10;
                                }
                                lines--;
                                i--;
                            }
                        }
                        sfb = sfbcnt + 1;
                        while (sfb < 12) {
                            temp = s[sfb];
                            int sb = s[sfb + 1] - temp;
                            int i = (temp << 2) - temp + j * sb;
                            for (; sb > 0; sb--) {
                                isPos[i] = scaleFactors[1].s[j][sfb];
                                if (isPos[i] != 7) {
                                    if (lsf) {
                                        iStereoKValues(isPos[i], ioType, i);
                                    } else {
                                        isRatio[i] = Constants.TAN12[isPos[i]];
                                    }
                                }
                                i++;
                            } // for (; sb>0 ...
                            sfb++;
                        } // while (sfb<12)
                        temp = s[10];
                        temp2 = s[11];
                        int sb = temp2 - temp;
                        sfb = (temp << 2) - temp + j * sb;
                        sb = s[12] - temp2;
                        int i = (temp2 << 2) - temp2 + j * sb;
                        for (; sb > 0; sb--) {
                            isPos[i] = isPos[sfb];
                            if (lsf) {
                                k0[i] = k0[sfb];
                                k1[i] = k1[sfb];
                            } else {
                                isRatio[i] = isRatio[sfb];
                            }
                            i++;
                        }
                    }
                }
            } else {
                int i = 31;
                int ss = 17;
                int sb = 0;
                while (i >= 0) {
                    if (ro1[i][ss] != 0.0f) {
                        sb = (i << 4) + (i << 1) + ss;
                        i = -1;
                    } else {
                        ss--;
                        if (ss < 0) {
                            i--;
                            ss = 17;
                        }
                    }
                }
                i = 0;
                while (l[i] <= sb) {
                    i++;
                }
                sfb = i;
                i = l[i];
                for (; sfb < 21; sfb++) {
                    sb = l[sfb + 1] - l[sfb];
                    for (; sb > 0; sb--) {
                        isPos[i] = scaleFactors[1].l[sfb];
                        if (isPos[i] != 7) {
                            if (lsf) {
                                iStereoKValues(isPos[i], ioType, i);
                            } else {
                                isRatio[i] = Constants.TAN12[isPos[i]];
                            }
                        }
                        i++;
                    }
                }
                sfb = l[20];
                for (sb = 576 - l[21]; (sb > 0) && (i < 576); sb--) {
                    isPos[i] = isPos[sfb]; // error here : i >=576
                    if (lsf) {
                        k0[i] = k0[sfb];
                        k1[i] = k1[sfb];
                    } else {
                        isRatio[i] = isRatio[sfb];
                    }
                    i++;
                }
            }
        }
        for (int sb = 0, i = 0; sb < SBLIMIT; sb++) {
            for (int ss = 0; ss < SSLIMIT; ss++) {
                if (isPos[i] == 7) {
                    if (msStereo) {
                        lr0[sb][ss] = (ro0[sb][ss] + ro1[sb][ss]) * 0.707106781f;
                        lr1[sb][ss] = (ro0[sb][ss] - ro1[sb][ss]) * 0.707106781f;
                    } else {
                        lr0[sb][ss] = ro0[sb][ss];
                        lr1[sb][ss] = ro1[sb][ss];
                    }
                } else if (iStereo) {
                    if (lsf) {
                        lr0[sb][ss] = ro0[sb][ss] * k0[i];
                        lr1[sb][ss] = ro0[sb][ss] * k1[i];
                    } else {
                        lr1[sb][ss] = ro0[sb][ss] / (1 + isRatio[i]);
                        lr0[sb][ss] = lr1[sb][ss] * isRatio[i];
                    }
                }
                i++;
            }
        }
    }

    private void antialias(int ch, int gr) {
        int sb18, ss, sb18lim;
        GrInfo gi = si.ch[ch].gr[gr];
        // 31 alias-reduction operations between each pair of sub-bands
        // with 8 butterflies between each pair
        if (gi.windowSwitching && (gi.blockType == 2) && !gi.mixedBlock) {
            return;
        }
        if (gi.windowSwitching && gi.mixedBlock && (gi.blockType == 2)) {
            sb18lim = 18;
        } else {
            sb18lim = 558;
        }
        for (sb18 = 0; sb18 < sb18lim; sb18 += 18) {
            for (ss = 0; ss < 8; ss++) {
                int srcIdx1 = sb18 + 17 - ss;
                int srcIdx2 = sb18 + 18 + ss;
                double bu = out1d[srcIdx1];
                double bd = out1d[srcIdx2];
                out1d[srcIdx1] = (bu * Constants.CS[ss]) - (bd * Constants.CA[ss]);
                out1d[srcIdx2] = (bd * Constants.CS[ss]) + (bu * Constants.CA[ss]);
            }
        }
    }

    private void hybrid(int ch, int gr) {
        GrInfo gi = si.ch[ch].gr[gr];
        for (int sb18 = 0; sb18 < 576; sb18 += 18) {
            int bt = (gi.windowSwitching && gi.mixedBlock && (sb18 < 36)) ? 0 : gi.blockType;
            double[] tsOut = out1d;
            double[] r = rawout;
            for (int cc = 0; cc < 18; cc++) {
                tsOutCopy[cc] = tsOut[cc + sb18];
            }
            fastInvMdct(tsOutCopy, r, bt);
            for (int cc = 0; cc < 18; cc++) {
                tsOut[cc + sb18] = tsOutCopy[cc];
            }
            // overlap addition
            double[] p = prevBlock[ch];
            tsOut[0 + sb18] = r[0] + p[sb18 + 0];
            p[sb18 + 0] = r[18];
            tsOut[1 + sb18] = r[1] + p[sb18 + 1];
            p[sb18 + 1] = r[19];
            tsOut[2 + sb18] = r[2] + p[sb18 + 2];
            p[sb18 + 2] = r[20];
            tsOut[3 + sb18] = r[3] + p[sb18 + 3];
            p[sb18 + 3] = r[21];
            tsOut[4 + sb18] = r[4] + p[sb18 + 4];
            p[sb18 + 4] = r[22];
            tsOut[5 + sb18] = r[5] + p[sb18 + 5];
            p[sb18 + 5] = r[23];
            tsOut[6 + sb18] = r[6] + p[sb18 + 6];
            p[sb18 + 6] = r[24];
            tsOut[7 + sb18] = r[7] + p[sb18 + 7];
            p[sb18 + 7] = r[25];
            tsOut[8 + sb18] = r[8] + p[sb18 + 8];
            p[sb18 + 8] = r[26];
            tsOut[9 + sb18] = r[9] + p[sb18 + 9];
            p[sb18 + 9] = r[27];
            tsOut[10 + sb18] = r[10] + p[sb18 + 10];
            p[sb18 + 10] = r[28];
            tsOut[11 + sb18] = r[11] + p[sb18 + 11];
            p[sb18 + 11] = r[29];
            tsOut[12 + sb18] = r[12] + p[sb18 + 12];
            p[sb18 + 12] = r[30];
            tsOut[13 + sb18] = r[13] + p[sb18 + 13];
            p[sb18 + 13] = r[31];
            tsOut[14 + sb18] = r[14] + p[sb18 + 14];
            p[sb18 + 14] = r[32];
            tsOut[15 + sb18] = r[15] + p[sb18 + 15];
            p[sb18 + 15] = r[33];
            tsOut[16 + sb18] = r[16] + p[sb18 + 16];
            p[sb18 + 16] = r[34];
            tsOut[17 + sb18] = r[17] + p[sb18 + 17];
            p[sb18 + 17] = r[35];
        }
    }

    private void fastInvMdct(double[] in, double[] out, int blockType) {
        double t0, t1, t2, t3, t4, t5, t6, t7, t8, t9;
        double t10, t11, t12, t13, t14, t15, t16, t17;
        if (blockType == 2) {
            for (int p = 0; p < 36; p += 9) {
                out[p] = out[p + 1] = out[p + 2] = out[p + 3] = out[p + 4] = 0.0f;
                out[p + 5] = out[p + 6] = out[p + 7] = out[p + 8] = 0.0f;
            }
            int sixI = 0;
            for (int i = 0; i < 3; i++) {
                // 12 point IMDCT
                // Begin 12 point IDCT
                // Input aliasing for 12 pt IDCT
                in[15 + i] += in[12 + i];
                in[12 + i] += in[9 + i];
                in[9 + i] += in[6 + i];
                in[6 + i] += in[3 + i];
                in[3 + i] += in[0 + i];
                // Input aliasing on odd indices (for 6 point IDCT)
                in[15 + i] += in[9 + i];
                in[9 + i] += in[3 + i];
                // 3 point IDCT on even indices
                double pp1, pp2, sum;
                pp2 = in[12 + i] * 0.500000000f;
                pp1 = in[6 + i] * 0.866025403f;
                sum = in[0 + i] + pp2;
                t1 = in[0 + i] - in[12 + i];
                t0 = sum + pp1;
                t2 = sum - pp1;
                // End 3 point IDCT on even indices
                // 3 point IDCT on odd indices (for 6 point IDCT)
                pp2 = in[15 + i] * 0.500000000f;
                pp1 = in[9 + i] * 0.866025403f;
                sum = in[3 + i] + pp2;
                t4 = in[3 + i] - in[15 + i];
                t5 = sum + pp1;
                t3 = sum - pp1;
                // End 3 point IDCT on odd indices
                // Twiddle factors on odd indices (for 6 point IDCT)
                t3 *= 1.931851653f;
                t4 *= 0.707106781f;
                t5 *= 0.517638090f;
                // Output butterflies on 2 3 point IDCT's (for 6 point IDCT)
                double save = t0;
                t0 += t5;
                t5 = save - t5;
                save = t1;
                t1 += t4;
                t4 = save - t4;
                save = t2;
                t2 += t3;
                t3 = save - t3;
                // End 6 point IDCT
                // Twiddle factors on indices (for 12 point IDCT)
                t0 *= 0.504314480f;
                t1 *= 0.541196100f;
                t2 *= 0.630236207f;
                t3 *= 0.821339815f;
                t4 *= 1.306562965f;
                t5 *= 3.830648788f;
                // End 12 point IDCT
                // Shift to 12 point modified IDCT, multiply by window type 2
                t8 = -t0 * 0.793353340f;
                t9 = -t0 * 0.608761429f;
                t7 = -t1 * 0.923879532f;
                t10 = -t1 * 0.382683432f;
                t6 = -t2 * 0.991444861f;
                t11 = -t2 * 0.130526192f;
                t0 = t3;
                t1 = t4 * 0.382683432f;
                t2 = t5 * 0.608761429f;
                t3 = -t5 * 0.793353340f;
                t4 = -t4 * 0.923879532f;
                t5 = -t0 * 0.991444861f;
                t0 *= 0.130526192f;
                out[sixI + 6] += t0;
                out[sixI + 7] += t1;
                out[sixI + 8] += t2;
                out[sixI + 9] += t3;
                out[sixI + 10] += t4;
                out[sixI + 11] += t5;
                out[sixI + 12] += t6;
                out[sixI + 13] += t7;
                out[sixI + 14] += t8;
                out[sixI + 15] += t9;
                out[sixI + 16] += t10;
                out[sixI + 17] += t11;
                sixI += 6;
            }
        } else {
            // 36 point IDCT
            // input aliasing for 36 point IDCT
            in[17] += in[16];
            in[16] += in[15];
            in[15] += in[14];
            in[14] += in[13];
            in[13] += in[12];
            in[12] += in[11];
            in[11] += in[10];
            in[10] += in[9];
            in[9] += in[8];
            in[8] += in[7];
            in[7] += in[6];
            in[6] += in[5];
            in[5] += in[4];
            in[4] += in[3];
            in[3] += in[2];
            in[2] += in[1];
            in[1] += in[0];
            // 18 point IDCT for odd indices
            // input aliasing for 18 point IDCT
            in[17] += in[15];
            in[15] += in[13];
            in[13] += in[11];
            in[11] += in[9];
            in[9] += in[7];
            in[7] += in[5];
            in[5] += in[3];
            in[3] += in[1];
            double tmp0, tmp1, tmp2, tmp3, tmp4, tmp0b, tmp1b, tmp2b, tmp3b;
            double tmp0o, tmp1o, tmp2o, tmp3o, tmp4o, tmp0ob, tmp1ob, tmp2ob, tmp3ob;
            // Fast 9 Point Inverse Discrete Cosine Transform
            //
            // By Francois-Raymond Boyer
            // mailto:boyerf@iro.umontreal.ca
            // http://www.iro.umontreal.ca/~boyerf
            //
            // The code has been optimized for Intel processors
            // (takes a lot of time to convert double to and from iternal FPU
            // representation)
            //
            // It is a simple "factorization" of the IDCT matrix.
            // 9 point IDCT on even indices
            // 5 points on odd indices (not really an IDCT)
            double i00 = in[0] + in[0];
            double iip12 = i00 + in[12];
            tmp0 = iip12 + in[4] * 1.8793852415718f + in[8] * 1.532088886238f + in[16] * 0.34729635533386f;
            tmp1 = i00 + in[4] - in[8] - in[12] - in[12] - in[16];
            tmp2 = iip12 - in[4] * 0.34729635533386f - in[8] * 1.8793852415718f + in[16] * 1.532088886238f;
            tmp3 = iip12 - in[4] * 1.532088886238f + in[8] * 0.34729635533386f - in[16] * 1.8793852415718f;
            tmp4 = in[0] - in[4] + in[8] - in[12] + in[16];
            // 4 points on even indices
            double i6s = in[6] * 1.732050808f; // Sqrt[3]
            tmp0b = in[2] * 1.9696155060244f + i6s + in[10] * 1.2855752193731f + in[14] * 0.68404028665134f;
            tmp1b = (in[2] - in[10] - in[14]) * 1.732050808f;
            tmp2b = in[2] * 1.2855752193731f - i6s - in[10] * 0.68404028665134f + in[14] * 1.9696155060244f;
            tmp3b = in[2] * 0.68404028665134f - i6s + in[10] * 1.9696155060244f - in[14] * 1.2855752193731f;
            // 9 point IDCT on odd indices
            // 5 points on odd indices (not really an IDCT)
            double i0 = in[0 + 1] + in[0 + 1];
            double i0p12 = i0 + in[12 + 1];
            tmp0o = i0p12 + in[4 + 1] * 1.8793852415718f + in[8 + 1] * 1.532088886238f + in[16 + 1] * 0.34729635533386f;
            tmp1o = i0 + in[4 + 1] - in[8 + 1] - in[12 + 1] - in[12 + 1] - in[16 + 1];
            tmp2o = i0p12 - in[4 + 1] * 0.34729635533386f - in[8 + 1] * 1.8793852415718f + in[16 + 1] * 1.532088886238f;
            tmp3o = i0p12 - in[4 + 1] * 1.532088886238f + in[8 + 1] * 0.34729635533386f - in[16 + 1] * 1.8793852415718f;
            tmp4o = (in[0 + 1] - in[4 + 1] + in[8 + 1] - in[12 + 1] + in[16 + 1]) * 0.707106781f; // Twiddled
            // 4 points on even indices
            double i7s = in[6 + 1] * 1.732050808f; // Sqrt[3]
            tmp0ob = in[2 + 1] * 1.9696155060244f + i7s + in[10 + 1] * 1.2855752193731f + in[14 + 1]
                    * 0.68404028665134f;
            tmp1ob = (in[2 + 1] - in[10 + 1] - in[14 + 1]) * 1.732050808f;
            tmp2ob = in[2 + 1] * 1.2855752193731f - i7s - in[10 + 1] * 0.68404028665134f + in[14 + 1]
                    * 1.9696155060244f;
            tmp3ob = in[2 + 1] * 0.68404028665134f - i7s + in[10 + 1] * 1.9696155060244f - in[14 + 1]
                    * 1.2855752193731f;
            // Twiddle factors on odd indices and
            // Butterflies on 9 point IDCT's and
            // twiddle factors for 36 point IDCT
            double e, o;
            e = tmp0 + tmp0b;
            o = (tmp0o + tmp0ob) * 0.501909918f;
            t0 = e + o;
            t17 = e - o;
            e = tmp1 + tmp1b;
            o = (tmp1o + tmp1ob) * 0.517638090f;
            t1 = e + o;
            t16 = e - o;
            e = tmp2 + tmp2b;
            o = (tmp2o + tmp2ob) * 0.551688959f;
            t2 = e + o;
            t15 = e - o;
            e = tmp3 + tmp3b;
            o = (tmp3o + tmp3ob) * 0.610387294f;
            t3 = e + o;
            t14 = e - o;
            t4 = tmp4 + tmp4o;
            t13 = tmp4 - tmp4o;
            e = tmp3 - tmp3b;
            o = (tmp3o - tmp3ob) * 0.871723397f;
            t5 = e + o;
            t12 = e - o;
            e = tmp2 - tmp2b;
            o = (tmp2o - tmp2ob) * 1.183100792f;
            t6 = e + o;
            t11 = e - o;
            e = tmp1 - tmp1b;
            o = (tmp1o - tmp1ob) * 1.931851653f;
            t7 = e + o;
            t10 = e - o;
            e = tmp0 - tmp0b;
            o = (tmp0o - tmp0ob) * 5.736856623f;
            t8 = e + o;
            t9 = e - o;
            // end 36 point IDCT */
            // shift to modified IDCT
            double[] win = Constants.WIN[blockType];
            out[0] = -t9 * win[0];
            out[1] = -t10 * win[1];
            out[2] = -t11 * win[2];
            out[3] = -t12 * win[3];
            out[4] = -t13 * win[4];
            out[5] = -t14 * win[5];
            out[6] = -t15 * win[6];
            out[7] = -t16 * win[7];
            out[8] = -t17 * win[8];
            out[9] = t17 * win[9];
            out[10] = t16 * win[10];
            out[11] = t15 * win[11];
            out[12] = t14 * win[12];
            out[13] = t13 * win[13];
            out[14] = t12 * win[14];
            out[15] = t11 * win[15];
            out[16] = t10 * win[16];
            out[17] = t9 * win[17];
            out[18] = t8 * win[18];
            out[19] = t7 * win[19];
            out[20] = t6 * win[20];
            out[21] = t5 * win[21];
            out[22] = t4 * win[22];
            out[23] = t3 * win[23];
            out[24] = t2 * win[24];
            out[25] = t1 * win[25];
            out[26] = t0 * win[26];
            out[27] = t0 * win[27];
            out[28] = t1 * win[28];
            out[29] = t2 * win[29];
            out[30] = t3 * win[30];
            out[31] = t4 * win[31];
            out[32] = t5 * win[32];
            out[33] = t6 * win[33];
            out[34] = t7 * win[34];
            out[35] = t8 * win[35];
        }
    }
}
